1. 팟캐스트로 듣는 코에이 삼국지 1

그것은 알기 싫다(XSFM) 팟캐스트에 최근 코에이 삼국지를 주제로 방송을 시작했다. 서양에 시오노 나나미 “로마인 이야기”가 있다면 동양은 나관중의 삼국지가 있다고 생각하는 한국인이 많다.

코에이 삼국지 1,2,3편을 직접 플레이하면 숫자와 문자만 가득하다. 삼국을 통일하기 위해서 몇달을 고생해서 밤을 새워가며 시간을 보낸 분들도 주변에서 부지기수로 볼 수 있고, 삼국지 게임을 더 잘 하기 위해서 삼국지를 게임을 한 후에 정독하는 사례도 심심치 않게 볼 수 있다.

이번에는 코에이 삼국지 게임 데이터를 추출하여 데이터를 분석해 보자.

2. 삼국지 실제 국력차이

삼국지의 조조, 유비, 손권이 게임에서는 대등하게 나오지만, 최근에 출시되는 삼국지는 실제 역사적 사실에 근거를 두고 나름 정확한 데이터가 담겨있다. 문명도 예외가 아닌데 양성균형, 서양, 백인, 남성 중심의 세계관을 조금 약화시키고 동양을 비롯한 제3세계와 여성의 비중을 늘리는 것도 사실이다.

위촉오 삼국간의 국력에 대한 주제를 다루었던 유튜브 동영상에 근거하여 실제 게임에서 데이터를 추출하여 비교분석해 보자.

3. 코에이 삼국지 13에 표현된 국력차이

3.1. 데이터 가져오기

코에이 삼국지 게임에 편집기를 붙여 데이터를 추출하여 엑셀로 저장한다. 다양한 데이터를 추출할 수 있지만, 인재와 관련된 장군 데이터와 주/도시와 관련된 데이터를 붙인다. 그리고 군단 정보에서 관우와 장로는 촉으로 장료와 공손공은 조조는 위로 손권은 오로 범주를 구분하고 결측값이 포함된 나머지는 분석에서 제외한다.

# 0. 환경설정 -----
library(tidyverse)
library(readxl)
library(DT)
library(treemap)
library(ggridges)
library(ggpubr)
library(d3scatter)
library(plotly)
library(crosstalk)
library(htmltools)
library(d3treeR)
library(extrafont)
loadfonts()

# 1. 데이터 가져오기 -----

city_dat <- read_excel("data/삼국지13.xlsx", sheet="도시")

# 2. 데이터 정제 -----

city_df <- city_dat %>% 
  select(지방, 도시, 주, 주도, 군단, 태수, 자금, 군량, 비병역인구, 병역인구, 상업, 농업, 문화) %>% 
  mutate(삼국 = case_when(군단 == "관우" ~ "촉",
                           군단 == "유비" ~ "촉",
                           군단 == "장로" ~ "촉",
                           군단 == "조조" ~ "위",
                           군단 == "장료" ~ "위",
                           군단 == "공손공" ~ "위",
                           군단 == "손권" ~ "오")) %>% 
  filter(!is.na(삼국)) %>% 
  mutate(인구 = 비병역인구 + 병역인구)

3.2. 국력으로 본 인구수

삼국 도시와 인구수를 먼저 뽑아 이를 표로 표현한다.

# 3. EDA - 도시수와 인구수 -----
## 3.1. 도시수
city_df %>% group_by(삼국) %>% 
  count(주) %>% 
  spread(삼국, n, fill=0)  %>% 
  datatable()

삼국 인구수를 좀더 상세히 살펴보면 다음과 같다.

## 3.2. 인구수
city_df %>% group_by(삼국) %>% 
  summarise(인구수 = sum(인구)) %>% 
  mutate(인구비율 = 인구수/ sum(인구수)) %>% 
  arrange(desc(인구수)) %>% 
  datatable() %>% 
    formatCurrency("인구수", currency="", digits=0) %>% 
    formatPercentage("인구비율", digits=1) 

삼국 도시와 인구수를 먼저 뽑은 자료를 treemap을 통해 시각화한다.

## 3.3. treemap: 삼국 - 주 - 도시 - 인구수
city_treemap_df <- city_df %>% group_by(삼국, 주, 도시) %>% 
  summarise(인구수 = sum(인구))

city_pop_treemap <- treemap(city_treemap_df, 
                              index = c("삼국", "주", "도시"),  
                              vSize = "인구수", 
                              vColor = "삼국",
                              title = "삼국지 국력 - 인구수", 
                              fontsize.labels=c(12, 8), 
                              draw = TRUE,
                              type = "categorical", 
                              palette = c("red", "blue", "green"),
                              fontfamily.title = "NanumGothic",
                              format.legend = list(scientific = FALSE, big.mark = ","),
                              title.legend="")

인터랙티브하게 동적으로 파악하면 좀더 쉽게 인구수 차이를 면밀하게 검토할 수 있다.

### 인구수 treemap 동적 그래프                 
d3tree2(city_pop_treemap, rootname = "삼국지")

3.3. 국력으로 본 농업

삼국 농업과 상업을 뽑아 동일한 방식으로 비교한다.

# 4. EDA - 농업/상업 -----
## 4.1. 농업
city_df %>% group_by(삼국, 주) %>% 
  summarise(도시수 = n(),
            농업 = sum(농업),
            상업 = sum(상업)) %>% 
  ungroup() %>% 
  mutate(농업비율 = 농업/ sum(농업),
         상업비율 = 상업/ sum(상업)) %>% 
  datatable() %>% 
  formatCurrency(c("농업","상업"), currency="", digits=0) %>% 
  formatPercentage(c("농업비율", "상업비율"), digits=1) 

삼국 농업을 뽑아 treemap으로 시각화한다.

## 3.3. treemap: 삼국 - 주 - 도시 - 농업

agr_treemap_df <- city_df %>% group_by(삼국, 주, 도시) %>% 
  summarise(농업 = sum(농업))

agr_treemap <- treemap(agr_treemap_df, 
                            index = c("삼국", "주", "도시"),  
                            vSize = "농업", 
                            vColor = "삼국",
                            title = "삼국지 국력 - 농업", 
                            fontsize.labels=c(12, 8), 
                            draw = TRUE,
                            type = "categorical", 
                            palette = c("red", "blue", "green"),
                            fontfamily.title = "NanumGothic",
                            format.legend = list(scientific = FALSE, big.mark = ","),
                            title.legend="")

인터랙티브하게 동적으로 파악하면 좀더 쉽게 농업 차이를 면밀하게 검토할 수 있다.

### 농업 treemap 동적 그래프                 
d3tree2(agr_treemap, rootname = "삼국지")

3.4. 국력으로 본 인재

인재는 무장 데이터를 가져와서 이를 도시 데이터와 붙여 위촉오 인재를 비교할 수 있도록 준비한다.

# 1. 데이터 가져오기 -----

general_dat <- read_excel("data/삼국지13.xlsx", sheet="무장")

# 2. 도시정보에서 장군 위촉오 소속 매칭
city_sam_mapping <- city_df %>% select(소재=도시, 삼국)

general_df <- general_dat %>% 
  select(무장, 군단, 소속, 소재, 성별, 통솔=원시통솔, 무력=원시무력, 지력=원시지력, 정치=원시정치, 생년, 등장년, 졸년, 혈연) %>% 
  left_join(city_sam_mapping) %>% 
  filter(!is.na(삼국)) %>% 
  mutate(나이 = 210 - 생년) %>% 
  mutate(삼국 = factor(삼국, levels=c("위", "오", "촉")))

general_df %>% 
  select(삼국, everything()) %>% 
  datatable()

위촉오에 속한 인재 숫자를 비교하고, 더 나아가 성비 균형을 맞추려고 노력한 국가를 파악한다. 오나라에 여성 인재가 촉이나 위나라와 비교하여 더 많다.

# 3. EDA - 인재 -----
## 3.1. 삼국 인재숫자 비교
general_df %>% count(삼국, sort=TRUE) %>% 
  mutate(인재비율 = n / sum(n)) %>% 
  rename(인재수=n) %>% 
  datatable() %>% 
    formatPercentage("인재비율", digits=1)
## 3.2. 성비
general_df %>% count(삼국, 성별) %>% 
  spread(성별, n) %>% 
  mutate(성비 = /(남+여)) %>% 
  datatable() %>% 
  formatPercentage("성비", digits=1)

210년을 기준으로 나이를 계산한다. 물론 210년 이후 상당기간 삼국관계가 지속되어 음의 나이를 갖는 인재도 있지만 전반적인 인재 등용에 대한 추세를 파악할 수는 있다. 즉, 촉나라는 지속적인 인재공급이 부족하여 고령화에 따른 국력약화가 시각적으로 파악된다.

# 4. 시각화 -----
## 4.1. 촉나라에 나타난 고령화
general_df %>% 
  ggplot(aes(x = 나이, y = 삼국, fill=삼국)) +
  geom_density_ridges(scale = 3, alpha=0.9) + 
  theme_minimal(base_size = 14, base_family = "NanumGothic") + 
  theme(legend.position = "none",
        axis.text=element_text(size=12)) +
  labs(x="기준나이: 210년", y="", title="삼국지 인재 나이분포") +
  guides(fill = guide_legend(nrow = 1)) +
  geom_vline(xintercept = 0) +
  scale_color_manual(values = c("red", "green", "blue"))

무엇보다 인재의 능력치가 삼국지를 관통하는 주된 흐름 중의 하나로 일기토의 추억을 누구나 갖고 있을 것이다. 이를 인터랙티브하게 살펴볼 수 있도록 추진해보자.

## 4.2. 능력치
### 4.2.1. 공유 데이터
general_sd_df <- general_df %>% 
  select(삼국, 무장, 소속, 통솔, 무력, 지력, 정치, 생년)

sam_sd <- SharedData$new(general_sd_df)

### 4.2.2. 제어
# year_ctrl <- filter_slider("생년", "출생년도", sam_sd, ~생년, width = "50%")
# sam_ctrl <- filter_checkbox("삼국", "위촉오", sam_sd, ~삼국, inline = TRUE)
filter_checkbox("삼국", "위촉오", sam_sd, ~삼국, inline = TRUE)
filter_slider("생년", "출생년도", sam_sd, ~생년, width = "50%")
### 4.2.3. 표
sam_tbl <- datatable(sam_sd, extensions="Scroller", 
                           style="bootstrap", 
                           class="compact", width="100%",
                           options=list(deferRender=TRUE, scrollY=300, scroller=TRUE))

### 4.2.4. 시각화
force_g <- ggplot(sam_sd, aes(x=무력, y=통솔, color=삼국,
                              text = paste('이름 :', 무장, "\n",
                                           '통솔:', 통솔, "\n",
                                           '무력:', 무력, "\n",
                                           '지력:', 지력, "\n",
                                           '정치:', 정치, "\n"))) +
  geom_point() +
  theme(legend.position = "none") +
  labs(x="무력", y="통솔") +
  scale_color_manual(values = c("red", "green", "blue"))

force_gg <- ggplotly(force_g, tooltip = "text")

### 4.2.5. 인터랙티브 시각화

bscols(widths = c(7,5),
       sam_tbl,
       force_gg)

  1. 256a. 팟캐문학관:코에이 삼국지(1/2) /이경혁